/*-------------------------------------------------------------------------
 *
 * sequence.c
 *	  PostgreSQL 序列支持代码。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/sequence.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/bufmask.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_sequence.h"
#include "catalog/pg_type.h"
#include "catalog/storage_xlog.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
#include "utils/varlena.h"


/*
 * 我们不想记录从序列中获取值的每一次操作，
 * 所以我们提前记录了一些获取操作。在发生崩溃时，
 * 我们可以跳过与我们预先记录数量相同的值。
 */
#define SEQ_LOG_VALS	32

/*
 * 序列的缓冲区页的“特殊区域”看起来是这样的。
 */
#define SEQ_MAGIC	  0x1717

typedef struct sequence_magic
{
	uint32		magic;
} sequence_magic;

/*
 * 我们为当前会话中接触过的每个序列存储一个 SeqTable 项。
 * 这需要保持 nextval/currval 状态。 （我们不能
 * 依赖 relcache，因为它只是一个缓存，可能决定
 * 丢弃条目。）
 */
typedef struct SeqTableData
{
	Oid			relid;			/* 此序列的 pg_class OID（哈希键） */
	Oid			filenode;		/* 此序列最后看到的 relfilenode */
	LocalTransactionId lxid;	/* 我们最后执行序列操作的事务 */
	bool		last_valid;		/* 我们有有效的“最后”值吗？ */
	int64		last;			/* last 返回的值 */
	int64		cached;			/* 已缓存的 last 值，用于 nextval */
	/* 如果 last != cached，我们尚未用尽所有缓存的值 */
	int64		increment;		/* 序列增量字段的副本 */
	/* 注意，增量在第一次执行 nextval_internal() 之前为零 */
} SeqTableData;

typedef SeqTableData *SeqTable;

static HTAB *seqhashtab = NULL; /* SeqTable 项的哈希表 */

/*
 * last_used_seq 由 nextval() 更新，以指向最后使用的
 * 序列。
 */
static SeqTableData *last_used_seq = NULL;

static void fc_fill_seq_with_data(Relation fc_rel, HeapTuple fc_tuple);
static void fc_fill_seq_fork_with_data(Relation fc_rel, HeapTuple fc_tuple, ForkNumber fc_forkNum);
static Relation fc_lock_and_open_sequence(SeqTable fc_seq);
static void fc_create_seq_hashtable(void);
static void fc_init_sequence(Oid fc_relid, SeqTable *fc_p_elm, Relation *fc_p_rel);
static Form_pg_sequence_data fc_read_seq_tuple(Relation fc_rel,
											Buffer *fc_buf, HeapTuple fc_seqdatatuple);
static void fc_init_params(ParseState *fc_pstate, List *fc_options, bool fc_for_identity,
						bool fc_isInit,
						Form_pg_sequence fc_seqform,
						Form_pg_sequence_data fc_seqdataform,
						bool *fc_need_seq_rewrite,
						List **fc_owned_by);
static void fc_do_setval(Oid fc_relid, int64 fc_next, bool fc_iscalled);
static void fc_process_owned_by(Relation fc_seqrel, List *fc_owned_by, bool fc_for_identity);


/*
 * DefineSequence
 *				创建一个新的序列关系
 */
ObjectAddress DefineSequence(ParseState *fc_pstate, CreateSeqStmt *fc_seq)
{
	FormData_pg_sequence fc_seqform;
	FormData_pg_sequence_data fc_seqdataform;
	bool		fc_need_seq_rewrite;
	List	   *fc_owned_by;
	CreateStmt *fc_stmt = makeNode(CreateStmt);
	Oid			fc_seqoid;
	ObjectAddress fc_address;
	Relation	fc_rel;
	HeapTuple	fc_tuple;
	TupleDesc	fc_tupDesc;
	Datum		fc_value[SEQ_COL_LASTCOL];
	bool		fc_null[SEQ_COL_LASTCOL];
	Datum		fc_pgs_values[Natts_pg_sequence];
	bool		fc_pgs_nulls[Natts_pg_sequence];
	int			fc_i;

	/*
	 * 如果给定了 if_not_exists 且同名关系已存在，则退出。
	 * （注意：当不使用 if_not_exists 时，我们不需要检查这一点，
	 * 因为 DefineRelation 会抱怨。）
	 */
	if (fc_seq->if_not_exists)
	{
		RangeVarGetAndCheckCreationNamespace(fc_seq->sequence, NoLock, &fc_seqoid);
		if (OidIsValid(fc_seqoid))
		{
			/*
			 * 如果我们处于扩展脚本中，必须确保预先存在的
			 * 对象是扩展的成员，以避免安全风险。
			 */
			ObjectAddressSet(fc_address, RelationRelationId, fc_seqoid);
			checkMembershipInCurrentExtension(&fc_address);

			/* 可以跳过 */
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_TABLE),
					 errmsg("relation \"%s\" already exists, skipping",
							fc_seq->sequence->relname)));
			return InvalidObjectAddress;
		}
	}

	/* 检查并设置所有选项值 */
	fc_init_params(fc_pstate, fc_seq->options, fc_seq->for_identity, true,
				&fc_seqform, &fc_seqdataform,
				&fc_need_seq_rewrite, &fc_owned_by);

	/*
	 * 创建关系（并填充元组的值[]和 null[]）
	 */
	fc_stmt->tableElts = NIL;
	for (fc_i = SEQ_COL_FIRSTCOL; fc_i <= SEQ_COL_LASTCOL; fc_i++)
	{
		ColumnDef  *fc_coldef = makeNode(ColumnDef);

		fc_coldef->inhcount = 0;
		fc_coldef->is_local = true;
		fc_coldef->is_not_null = true;
		fc_coldef->is_from_type = false;
		fc_coldef->storage = 0;
		fc_coldef->raw_default = NULL;
		fc_coldef->cooked_default = NULL;
		fc_coldef->collClause = NULL;
		fc_coldef->collOid = InvalidOid;
		fc_coldef->constraints = NIL;
		fc_coldef->location = -1;

		fc_null[fc_i - 1] = false;

		switch (fc_i)
		{
			case SEQ_COL_LASTVAL:
				fc_coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
				fc_coldef->colname = "last_value";
				fc_value[fc_i - 1] = Int64GetDatumFast(fc_seqdataform.last_value);
				break;
			case SEQ_COL_LOG:
				fc_coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
				fc_coldef->colname = "log_cnt";
				fc_value[fc_i - 1] = Int64GetDatum((int64) 0);
				break;
			case SEQ_COL_CALLED:
				fc_coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
				fc_coldef->colname = "is_called";
				fc_value[fc_i - 1] = BoolGetDatum(false);
				break;
		}
		fc_stmt->tableElts = lappend(fc_stmt->tableElts, fc_coldef);
	}

	fc_stmt->relation = fc_seq->sequence;
	fc_stmt->inhRelations = NIL;
	fc_stmt->constraints = NIL;
	fc_stmt->options = NIL;
	fc_stmt->oncommit = ONCOMMIT_NOOP;
	fc_stmt->tablespacename = NULL;
	fc_stmt->if_not_exists = fc_seq->if_not_exists;

	fc_address = DefineRelation(fc_stmt, RELKIND_SEQUENCE, fc_seq->ownerId, NULL, NULL);
	fc_seqoid = fc_address.objectId;
	Assert(fc_seqoid != InvalidOid);

	fc_rel = table_open(fc_seqoid, AccessExclusiveLock);
	fc_tupDesc = RelationGetDescr(fc_rel);

	/* 现在初始化序列的数据 */
	fc_tuple = heap_form_tuple(fc_tupDesc, fc_value, fc_null);
	fc_fill_seq_with_data(fc_rel, fc_tuple);

	/* 处理 OWNED BY（如果提供） */
	if (fc_owned_by)
		fc_process_owned_by(fc_rel, fc_owned_by, fc_seq->for_identity);

	table_close(fc_rel, NoLock);

	/* 填充 pg_sequence */
	fc_rel = table_open(SequenceRelationId, RowExclusiveLock);
	fc_tupDesc = RelationGetDescr(fc_rel);

	memset(fc_pgs_nulls, 0, sizeof(fc_pgs_nulls));

	fc_pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(fc_seqoid);
	fc_pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(fc_seqform.seqtypid);
	fc_pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(fc_seqform.seqstart);
	fc_pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(fc_seqform.seqincrement);
	fc_pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(fc_seqform.seqmax);
	fc_pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(fc_seqform.seqmin);
	fc_pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(fc_seqform.seqcache);
	fc_pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(fc_seqform.seqcycle);

	fc_tuple = heap_form_tuple(fc_tupDesc, fc_pgs_values, fc_pgs_nulls);
	CatalogTupleInsert(fc_rel, fc_tuple);

	heap_freetuple(fc_tuple);
	table_close(fc_rel, RowExclusiveLock);

	return fc_address;
}

/*
 * 将序列重置为其初始值。
 *
 * 此更改是事务性进行的，因此在当前事务失败时，
 * 序列将恢复到其先前状态。
 * 我们通过为序列创建一个全新的 relfilenode 来实现这一点；因此这与
 * ALTER TABLE 的重写形式非常相似。
 *
 * 假定调用者已经对序列获取了 AccessExclusiveLock，
 * 该锁必须在事务结束之前不被释放。 调用者也
 * 负责权限检查。
 */
void ResetSequence(Oid fc_seq_relid)
{
	Relation	fc_seq_rel;
	SeqTable	fc_elm;
	Form_pg_sequence_data fc_seq;
	Buffer		fc_buf;
	HeapTupleData fc_seqdatatuple;
	HeapTuple	fc_tuple;
	HeapTuple	fc_pgstuple;
	Form_pg_sequence fc_pgsform;
	int64		fc_startv;

	/*
	 * 读取旧序列。 这比实际所需的工作多一点，
	 * 但这很简单，我们确实想要再次确认它确实是一个序列。
	 */
	fc_init_sequence(fc_seq_relid, &fc_elm, &fc_seq_rel);
	(void) fc_read_seq_tuple(fc_seq_rel, &fc_buf, &fc_seqdatatuple);

	fc_pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(fc_seq_relid));
	if (!HeapTupleIsValid(fc_pgstuple))
		elog(ERROR, "cache lookup failed for sequence %u", fc_seq_relid);
	fc_pgsform = (Form_pg_sequence) GETSTRUCT(fc_pgstuple);
	fc_startv = fc_pgsform->seqstart;
	ReleaseSysCache(fc_pgstuple);

	/*
	 * 复制现有的序列元组。
	 */
	fc_tuple = heap_copytuple(&fc_seqdatatuple);

	/* 现在我们已完成对旧页面的操作 */
	UnlockReleaseBuffer(fc_buf);

	/*
	 * 修改已复制的元组以执行重启（请比较 AlterSequence 中的 RESTART
	 * 操作）
	 */
	fc_seq = (Form_pg_sequence_data) GETSTRUCT(fc_tuple);
	fc_seq->last_value = fc_startv;
	fc_seq->is_called = false;
	fc_seq->log_cnt = 0;

	/*
	 * 为序列创建一个新的存储文件。
	 */
	RelationSetNewRelfilenode(fc_seq_rel, fc_seq_rel->rd_rel->relpersistence);

	/*
	 * 确保序列的 relfrozenxid 为 0，因为它不会包含任何
	 * 未冻结的 XIDs。 relminmxid 也是相同，因为序列将永远
	 * 不包含多事务。
	 */
	Assert(fc_seq_rel->rd_rel->relfrozenxid == InvalidTransactionId);
	Assert(fc_seq_rel->rd_rel->relminmxid == InvalidMultiXactId);

	/*
	 * 将修改后的元组插入到新的存储文件中。
	 */
	fc_fill_seq_with_data(fc_seq_rel, fc_tuple);

	/* 清除本地缓存，以便我们不认为我们有缓存的数字 */
	/* 注意我们不改变 currval() 状态 */
	fc_elm->cached = fc_elm->last;

	relation_close(fc_seq_rel, NoLock);
}

/*
 * 用指定的元组作为内容初始化序列的关系
 *
 * 这通过根据需要同时写入主分区和初始化分区来处理未记录序列。
 */
static void fc_fill_seq_with_data(Relation fc_rel, HeapTuple fc_tuple)
{
	fc_fill_seq_fork_with_data(fc_rel, fc_tuple, MAIN_FORKNUM);

	if (fc_rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
	{
		SMgrRelation fc_srel;

		fc_srel = smgropen(fc_rel->rd_node, InvalidBackendId);
		smgrcreate(fc_srel, INIT_FORKNUM, false);
		log_smgrcreate(&fc_rel->rd_node, INIT_FORKNUM);
		fc_fill_seq_fork_with_data(fc_rel, fc_tuple, INIT_FORKNUM);
		FlushRelationBuffers(fc_rel);
		smgrclose(fc_srel);
	}
}

/*
 * 用指定的元组作为内容初始化序列的关系分区
 */
static void fc_fill_seq_fork_with_data(Relation fc_rel, HeapTuple fc_tuple, ForkNumber fc_forkNum)
{
	Buffer		fc_buf;
	Page		fc_page;
	sequence_magic *fc_sm;
	OffsetNumber fc_offnum;

	/* 用特殊的魔法数字初始化关系的第一页 */

	fc_buf = ReadBufferExtended(fc_rel, fc_forkNum, P_NEW, RBM_NORMAL, NULL);
	Assert(BufferGetBlockNumber(fc_buf) == 0);

	fc_page = BufferGetPage(fc_buf);

	PageInit(fc_page, BufferGetPageSize(fc_buf), sizeof(sequence_magic));
	fc_sm = (sequence_magic *) PageGetSpecialPointer(fc_page);
	fc_sm->magic = SEQ_MAGIC;

	/* 现在插入序列元组 */

	LockBuffer(fc_buf, BUFFER_LOCK_EXCLUSIVE);

	/*
	 * 由于 VACUUM 不处理序列，我们必须强制元组现在具有
	 * xmin = FrozenTransactionId。否则，在 2G 事务之后，它将变得
	 * 对 SELECTs 不可见。这样做是可以的，因为如果当前事务回滚，则没有其他事务会
	 * 检查序列元组。
	 */
	HeapTupleHeaderSetXmin(fc_tuple->t_data, FrozenTransactionId);
	HeapTupleHeaderSetXminFrozen(fc_tuple->t_data);
	HeapTupleHeaderSetCmin(fc_tuple->t_data, FirstCommandId);
	HeapTupleHeaderSetXmax(fc_tuple->t_data, InvalidTransactionId);
	fc_tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
	ItemPointerSet(&fc_tuple->t_data->t_ctid, 0, FirstOffsetNumber);

	/* 检查 nextval_internal() 的等效调用上方的注释。 */
	if (RelationNeedsWAL(fc_rel))
		GetTopTransactionId();

	START_CRIT_SECTION();

	MarkBufferDirty(fc_buf);

	fc_offnum = PageAddItem(fc_page, (Item) fc_tuple->t_data, fc_tuple->t_len,
						 InvalidOffsetNumber, false, false);
	if (fc_offnum != FirstOffsetNumber)
		elog(ERROR, "failed to add sequence tuple to page");

	/* XLOG 相关内容 */
	if (RelationNeedsWAL(fc_rel) || fc_forkNum == INIT_FORKNUM)
	{
		xl_seq_rec	fc_xlrec;
		XLogRecPtr	fc_recptr;

		XLogBeginInsert();
		XLogRegisterBuffer(0, fc_buf, REGBUF_WILL_INIT);

		fc_xlrec.node = fc_rel->rd_node;

		XLogRegisterData((char *) &fc_xlrec, sizeof(xl_seq_rec));
		XLogRegisterData((char *) fc_tuple->t_data, fc_tuple->t_len);

		fc_recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);

		PageSetLSN(fc_page, fc_recptr);
	}

	END_CRIT_SECTION();

	UnlockReleaseBuffer(fc_buf);
}

/*
 * AlterSequence
 *
 * 修改序列关系的定义
 */
ObjectAddress AlterSequence(ParseState *fc_pstate, AlterSeqStmt *fc_stmt)
{
	Oid			fc_relid;
	SeqTable	fc_elm;
	Relation	fc_seqrel;
	Buffer		fc_buf;
	HeapTupleData fc_datatuple;
	Form_pg_sequence fc_seqform;
	Form_pg_sequence_data fc_newdataform;
	bool		fc_need_seq_rewrite;
	List	   *fc_owned_by;
	ObjectAddress fc_address;
	Relation	fc_rel;
	HeapTuple	fc_seqtuple;
	HeapTuple	fc_newdatatuple;

	/* 打开并锁定序列，并在此过程中检查所有权。 */
	fc_relid = RangeVarGetRelidExtended(fc_stmt->sequence,
									 ShareRowExclusiveLock,
									 fc_stmt->missing_ok ? RVR_MISSING_OK : 0,
									 RangeVarCallbackOwnsRelation,
									 NULL);
	if (fc_relid == InvalidOid)
	{
		ereport(NOTICE,
				(errmsg("relation \"%s\" does not exist, skipping",
						fc_stmt->sequence->relname)));
		return InvalidObjectAddress;
	}

	fc_init_sequence(fc_relid, &fc_elm, &fc_seqrel);

	fc_rel = table_open(SequenceRelationId, RowExclusiveLock);
	fc_seqtuple = SearchSysCacheCopy1(SEQRELID,
								   ObjectIdGetDatum(fc_relid));
	if (!HeapTupleIsValid(fc_seqtuple))
		elog(ERROR, "cache lookup failed for sequence %u",
			 fc_relid);

	fc_seqform = (Form_pg_sequence) GETSTRUCT(fc_seqtuple);

	/* 锁定页面的缓冲区并将元组读入新的序列结构 */
	(void) fc_read_seq_tuple(fc_seqrel, &fc_buf, &fc_datatuple);

	/* 复制现有的序列数据元组，以便可以在本地修改 */
	fc_newdatatuple = heap_copytuple(&fc_datatuple);
	fc_newdataform = (Form_pg_sequence_data) GETSTRUCT(fc_newdatatuple);

	UnlockReleaseBuffer(fc_buf);

	/* 检查并设置新值 */
	fc_init_params(fc_pstate, fc_stmt->options, fc_stmt->for_identity, false,
				fc_seqform, fc_newdataform,
				&fc_need_seq_rewrite, &fc_owned_by);

	/* 清除本地缓存，以便我们不认为我们有缓存的数字 */
	/* 注意我们不改变 currval() 状态 */
	fc_elm->cached = fc_elm->last;

	/* 如有必要，重写序列关系本身 */
	if (fc_need_seq_rewrite)
	{
		/* 检查 nextval_internal() 的等效调用上方的注释。 */
		if (RelationNeedsWAL(fc_seqrel))
			GetTopTransactionId();

		/*
		 * 为序列创建一个新的存储文件，使状态
		 * 更改为事务性的。
		 */
		RelationSetNewRelfilenode(fc_seqrel, fc_seqrel->rd_rel->relpersistence);

		/*
		 * 确保序列的 relfrozenxid 为 0，因为它不会包含任何
		 * 未冻结的 XIDs。relminmxid 也是如此，因为序列永远不会
		 * 包含多事务。
		 */
		Assert(fc_seqrel->rd_rel->relfrozenxid == InvalidTransactionId);
		Assert(fc_seqrel->rd_rel->relminmxid == InvalidMultiXactId);

		/*
		 * 将修改后的元组插入到新的存储文件中。
		 */
		fc_fill_seq_with_data(fc_seqrel, fc_newdatatuple);
	}

	/* 处理 OWNED BY（如果提供） */
	if (fc_owned_by)
		fc_process_owned_by(fc_seqrel, fc_owned_by, fc_stmt->for_identity);

	/* 更新 pg_sequence 元组（在某些情况下我们可以跳过这一步......） */
	CatalogTupleUpdate(fc_rel, &fc_seqtuple->t_self, fc_seqtuple);

	InvokeObjectPostAlterHook(RelationRelationId, fc_relid, 0);

	ObjectAddressSet(fc_address, RelationRelationId, fc_relid);

	table_close(fc_rel, RowExclusiveLock);
	relation_close(fc_seqrel, NoLock);

	return fc_address;
}

void SequenceChangePersistence(Oid fc_relid, char fc_newrelpersistence)
{
	SeqTable	fc_elm;
	Relation	fc_seqrel;
	Buffer		fc_buf;
	HeapTupleData fc_seqdatatuple;

	/*
	 * ALTER SEQUENCE 会在之前获取这个锁。 如果我们正在处理一个用于
	 * ALTER TABLE 的被拥有序列，现在锁定。在没有锁的情况下，我们将
	 * 丢弃从 nextval() 调用（在其他会话中）到此函数的缓冲区解锁和
	 * 此事务的提交之间的增量。
	 */
	LockRelationOid(fc_relid, AccessExclusiveLock);
	fc_init_sequence(fc_relid, &fc_elm, &fc_seqrel);

	/* 检查 nextval_internal() 的等效调用上方的注释。 */
	if (RelationNeedsWAL(fc_seqrel))
		GetTopTransactionId();

	(void) fc_read_seq_tuple(fc_seqrel, &fc_buf, &fc_seqdatatuple);
	RelationSetNewRelfilenode(fc_seqrel, fc_newrelpersistence);
	fc_fill_seq_with_data(fc_seqrel, &fc_seqdatatuple);
	UnlockReleaseBuffer(fc_buf);

	relation_close(fc_seqrel, NoLock);
}

void DeleteSequenceTuple(Oid fc_relid)
{
	Relation	fc_rel;
	HeapTuple	fc_tuple;

	fc_rel = table_open(SequenceRelationId, RowExclusiveLock);

	fc_tuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(fc_relid));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for sequence %u", fc_relid);

	CatalogTupleDelete(fc_rel, &fc_tuple->t_self);

	ReleaseSysCache(fc_tuple);
	table_close(fc_rel, RowExclusiveLock);
}

/*
 * 注意：带有文本参数的 nextval 不再作为 pg_proc 
 * 条目导出，但我们保留它以简化可能直接调用该函数的 C 代码的移植。
 */
Datum nextval(PG_FUNCTION_ARGS)
{
	text	   *fc_seqin = PG_GETARG_TEXT_PP(0);
	RangeVar   *fc_sequence;
	Oid			fc_relid;

	fc_sequence = makeRangeVarFromNameList(textToQualifiedNameList(fc_seqin));

	/*
	 * XXX: 在并发DDL的情况下这是不安全的，但在这里获取锁的代价比让nextval_internal去做更高，因为后者维护一个缓存，使我们在每个事务中不会多次接触锁管理器。目前并不清楚性能惩罚在实践中是否显著，但现在我们就这样做。
	 */
	fc_relid = RangeVarGetRelid(fc_sequence, NoLock, false);

	PG_RETURN_INT64(nextval_internal(fc_relid, true));
}

Datum nextval_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);

	PG_RETURN_INT64(nextval_internal(fc_relid, true));
}

int64 nextval_internal(Oid fc_relid, bool fc_check_permissions)
{
	SeqTable	fc_elm;
	Relation	fc_seqrel;
	Buffer		fc_buf;
	Page		fc_page;
	HeapTuple	fc_pgstuple;
	Form_pg_sequence fc_pgsform;
	HeapTupleData fc_seqdatatuple;
	Form_pg_sequence_data fc_seq;
	int64		fc_incby,
				fc_maxv,
				fc_minv,
				fc_cache,
				fc_log,
				fc_fetch,
				fc_last;
	int64		fc_result,
				fc_next,
				fc_rescnt = 0;
	bool		fc_cycle;
	bool		fc_logit = false;

	/* 打开并锁定序列 */
	fc_init_sequence(fc_relid, &fc_elm, &fc_seqrel);

	if (fc_check_permissions &&
		pg_class_aclcheck(fc_elm->relid, GetUserId(),
						  ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied for sequence %s",
						RelationGetRelationName(fc_seqrel))));

	/* 只读事务只能修改临时序列 */
	if (!fc_seqrel->rd_islocaltemp)
		PreventCommandIfReadOnly("nextval()");

	/*
	 * 在并行操作期间禁止这样做，因为为了让它工作，协作的后端需要共享后端本地缓存的序列信息。目前我们不支持这一点。
	 */
	PreventCommandIfParallelMode("nextval()");

	if (fc_elm->last != fc_elm->cached)	/* 一些数字已被缓存 */
	{
		Assert(fc_elm->last_valid);
		Assert(fc_elm->increment != 0);
		fc_elm->last += fc_elm->increment;
		relation_close(fc_seqrel, NoLock);
		last_used_seq = fc_elm;
		return fc_elm->last;
	}

	fc_pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(fc_relid));
	if (!HeapTupleIsValid(fc_pgstuple))
		elog(ERROR, "cache lookup failed for sequence %u", fc_relid);
	fc_pgsform = (Form_pg_sequence) GETSTRUCT(fc_pgstuple);
	fc_incby = fc_pgsform->seqincrement;
	fc_maxv = fc_pgsform->seqmax;
	fc_minv = fc_pgsform->seqmin;
	fc_cache = fc_pgsform->seqcache;
	fc_cycle = fc_pgsform->seqcycle;
	ReleaseSysCache(fc_pgstuple);

	/* 锁定页面的缓冲区并读取元组 */
	fc_seq = fc_read_seq_tuple(fc_seqrel, &fc_buf, &fc_seqdatatuple);
	fc_page = BufferGetPage(fc_buf);

	fc_elm->increment = fc_incby;
	fc_last = fc_next = fc_result = fc_seq->last_value;
	fc_fetch = fc_cache;
	fc_log = fc_seq->log_cnt;

	if (!fc_seq->is_called)
	{
		fc_rescnt++;				/* 如果未被调用则返回last_value */
		fc_fetch--;
	}

	/*
	 * 决定我们是否应该生成WAL日志记录。如果是，则强制增加获取计数，以抓取SEQ_LOG_VALS比我们实际需要缓存的更多的值。（这些值在不记录的情况下是可以使用的。）
	 *
	 * 如果这是检查点后的第一个nextval，我们必须强制写入新的WAL记录，否则从检查点开始重放将无法使序列超过已记录的值。在这种情况下，我们不妨获取额外的值。
	 */
	if (fc_log < fc_fetch || !fc_seq->is_called)
	{
		/* 强制日志以满足对值的本地需求 */
		fc_fetch = fc_log = fc_fetch + SEQ_LOG_VALS;
		fc_logit = true;
	}
	else
	{
		XLogRecPtr	fc_redoptr = GetRedoRecPtr();

		if (PageGetLSN(fc_page) <= fc_redoptr)
		{
			/* seq的最后更新时间在检查点之前 */
			fc_fetch = fc_log = fc_fetch + SEQ_LOG_VALS;
			fc_logit = true;
		}
	}

	while (fc_fetch)				/* 尝试获取缓存 [+ 日志 ] 数字 */
	{
		/*
		 * 检查上升序列的MAXVALUE和下降序列的MINVALUE
		 */
		if (fc_incby > 0)
		{
			/* 上升序列 */
			if ((fc_maxv >= 0 && fc_next > fc_maxv - fc_incby) ||
				(fc_maxv < 0 && fc_next + fc_incby > fc_maxv))
			{
				if (fc_rescnt > 0)
					break;		/* 停止获取 */
				if (!fc_cycle)
					ereport(ERROR,
							(errcode(ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED),
							 errmsg("nextval: reached maximum value of sequence \"%s\" (%lld)",
									RelationGetRelationName(fc_seqrel),
									(long long) fc_maxv)));
				fc_next = fc_minv;
			}
			else
				fc_next += fc_incby;
		}
		else
		{
			/* 下降序列 */
			if ((fc_minv < 0 && fc_next < fc_minv - fc_incby) ||
				(fc_minv >= 0 && fc_next + fc_incby < fc_minv))
			{
				if (fc_rescnt > 0)
					break;		/* 停止获取 */
				if (!fc_cycle)
					ereport(ERROR,
							(errcode(ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED),
							 errmsg("nextval: reached minimum value of sequence \"%s\" (%lld)",
									RelationGetRelationName(fc_seqrel),
									(long long) fc_minv)));
				fc_next = fc_maxv;
			}
			else
				fc_next += fc_incby;
		}
		fc_fetch--;
		if (fc_rescnt < fc_cache)
		{
			fc_log--;
			fc_rescnt++;
			fc_last = fc_next;
			if (fc_rescnt == 1)	/* 如果这是第一个结果 - */
				fc_result = fc_next;	/* 这是要返回的内容 */
		}
	}

	fc_log -= fc_fetch;				/* 调整未获取的数字 */
	Assert(fc_log >= 0);

	/* 将信息保存在本地缓存中 */
	fc_elm->last = fc_result;			/* 最后返回的数字 */
	fc_elm->cached = fc_last;			/* 最后获取的数字 */
	fc_elm->last_valid = true;

	last_used_seq = fc_elm;

	/*
	 * 如果需要进行WAL日志记录，获取一个xid，以便这个事务的提交将触发WAL刷新并等待syncrep。确保顶层事务有一个xid就足够了，无需为子事务分配xids，这将触发适当的等待。（必须在这里做到，因此我们在关键区外）
	 */
	if (fc_logit && RelationNeedsWAL(fc_seqrel))
		GetTopTransactionId();

	/* 准备更改磁盘上的（或实际上是缓冲区中的）元组 */
	START_CRIT_SECTION();

	/*
	 * 我们必须在执行XLogInsert()之前将缓冲区标记为脏；请参见SyncOneBuffer()中的注释。然而，我们不会立即应用所需的更改。这看起来像是违反了缓冲区更新协议，但实际上是安全的，因为我们对缓冲区持有独占锁。任何其他进程，包括检查点，尝试检查缓冲区内容时，将阻塞直至我们释放锁，然后将看到我们在下面安装的最终状态。
	 */
	MarkBufferDirty(fc_buf);

	/* XLOG 相关内容 */
	if (fc_logit && RelationNeedsWAL(fc_seqrel))
	{
		xl_seq_rec	fc_xlrec;
		XLogRecPtr	fc_recptr;

		/*
		 * 我们并不记录元组的当前状态，而是记录
		 * 在“日志”更多提取后出现的状态。这让我们可以跳过
		 * 这么多未来的WAL记录，代价是如果我们崩溃，会丢失这些
		 * 序列值。
		 */
		XLogBeginInsert();
		XLogRegisterBuffer(0, fc_buf, REGBUF_WILL_INIT);

		/* 设置将在xlog中保存的值 */
		fc_seq->last_value = fc_next;
		fc_seq->is_called = true;
		fc_seq->log_cnt = 0;

		fc_xlrec.node = fc_seqrel->rd_node;

		XLogRegisterData((char *) &fc_xlrec, sizeof(xl_seq_rec));
		XLogRegisterData((char *) fc_seqdatatuple.t_data, fc_seqdatatuple.t_len);

		fc_recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);

		PageSetLSN(fc_page, fc_recptr);
	}

	/* 现在更新序列元组到预期的最终状态 */
	fc_seq->last_value = fc_last;		/* 最后获取的数字 */
	fc_seq->is_called = true;
	fc_seq->log_cnt = fc_log;			/* 已记录多少 */

	END_CRIT_SECTION();

	UnlockReleaseBuffer(fc_buf);

	relation_close(fc_seqrel, NoLock);

	return fc_result;
}

Datum currval_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);
	int64		fc_result;
	SeqTable	fc_elm;
	Relation	fc_seqrel;

	/* 打开并锁定序列 */
	fc_init_sequence(fc_relid, &fc_elm, &fc_seqrel);

	if (pg_class_aclcheck(fc_elm->relid, GetUserId(),
						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied for sequence %s",
						RelationGetRelationName(fc_seqrel))));

	if (!fc_elm->last_valid)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
						RelationGetRelationName(fc_seqrel))));

	fc_result = fc_elm->last;

	relation_close(fc_seqrel, NoLock);

	PG_RETURN_INT64(fc_result);
}

Datum lastval(PG_FUNCTION_ARGS)
{
	Relation	fc_seqrel;
	int64		fc_result;

	if (last_used_seq == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("lastval is not yet defined in this session")));

	/* 自上次nextval()以来，可能有人已经删除了序列 */
	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(last_used_seq->relid)))
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("lastval is not yet defined in this session")));

	fc_seqrel = fc_lock_and_open_sequence(last_used_seq);

	/* 必须已经为此序列调用了nextval() */
	Assert(last_used_seq->last_valid);

	if (pg_class_aclcheck(last_used_seq->relid, GetUserId(),
						  ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied for sequence %s",
						RelationGetRelationName(fc_seqrel))));

	fc_result = last_used_seq->last;
	relation_close(fc_seqrel, NoLock);

	PG_RETURN_INT64(fc_result);
}

/*
 * 处理SETVAL的2和3参数形式的主要内部过程。
 *
 * 请注意，3参数版本（设置is_called标志）仅
 * 用于pg_dump，设置is_called标志可能不起作用，
 * 如果多个用户连接到数据库并引用
 * 该序列（如果pg_dump正在恢复它，这种情况不太可能）。
 *
 * 必须有3参数版本，以便pg_dump可以
 * 在仅数据恢复期间精确恢复序列的状态 -
 * 这是在现有序列中清除is_called标志的唯一方法。
 */
static void fc_do_setval(Oid fc_relid, int64 fc_next, bool fc_iscalled)
{
	SeqTable	fc_elm;
	Relation	fc_seqrel;
	Buffer		fc_buf;
	HeapTupleData fc_seqdatatuple;
	Form_pg_sequence_data fc_seq;
	HeapTuple	fc_pgstuple;
	Form_pg_sequence fc_pgsform;
	int64		fc_maxv,
				fc_minv;

	/* 打开并锁定序列 */
	fc_init_sequence(fc_relid, &fc_elm, &fc_seqrel);

	if (pg_class_aclcheck(fc_elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied for sequence %s",
						RelationGetRelationName(fc_seqrel))));

	fc_pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(fc_relid));
	if (!HeapTupleIsValid(fc_pgstuple))
		elog(ERROR, "cache lookup failed for sequence %u", fc_relid);
	fc_pgsform = (Form_pg_sequence) GETSTRUCT(fc_pgstuple);
	fc_maxv = fc_pgsform->seqmax;
	fc_minv = fc_pgsform->seqmin;
	ReleaseSysCache(fc_pgstuple);

	/* 只读事务只能修改临时序列 */
	if (!fc_seqrel->rd_islocaltemp)
		PreventCommandIfReadOnly("setval()");

	/*
	 * 在并行操作期间禁止这样做，因为为了让它工作，协作的后端需要共享后端本地缓存的序列信息。目前我们不支持这一点。
	 */
	PreventCommandIfParallelMode("setval()");

	/* 锁定页面的缓冲区并读取元组 */
	fc_seq = fc_read_seq_tuple(fc_seqrel, &fc_buf, &fc_seqdatatuple);

	if ((fc_next < fc_minv) || (fc_next > fc_maxv))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("setval: value %lld is out of bounds for sequence \"%s\" (%lld..%lld)",
						(long long) fc_next, RelationGetRelationName(fc_seqrel),
						(long long) fc_minv, (long long) fc_maxv)));

	/* 仅当iscalled=true时设置currval()状态 */
	if (fc_iscalled)
	{
		fc_elm->last = fc_next;		/* 最后返回的数字 */
		fc_elm->last_valid = true;
	}

	/* 在任何情况下，忘记未来的缓存数字 */
	fc_elm->cached = fc_elm->last;

	/* 检查 nextval_internal() 的等效调用上方的注释。 */
	if (RelationNeedsWAL(fc_seqrel))
		GetTopTransactionId();

	/* 准备更改磁盘上的（或实际上是缓冲区中的）元组 */
	START_CRIT_SECTION();

	fc_seq->last_value = fc_next;		/* 最后获取的数字 */
	fc_seq->is_called = fc_iscalled;
	fc_seq->log_cnt = 0;

	MarkBufferDirty(fc_buf);

	/* XLOG 相关内容 */
	if (RelationNeedsWAL(fc_seqrel))
	{
		xl_seq_rec	fc_xlrec;
		XLogRecPtr	fc_recptr;
		Page		fc_page = BufferGetPage(fc_buf);

		XLogBeginInsert();
		XLogRegisterBuffer(0, fc_buf, REGBUF_WILL_INIT);

		fc_xlrec.node = fc_seqrel->rd_node;
		XLogRegisterData((char *) &fc_xlrec, sizeof(xl_seq_rec));
		XLogRegisterData((char *) fc_seqdatatuple.t_data, fc_seqdatatuple.t_len);

		fc_recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);

		PageSetLSN(fc_page, fc_recptr);
	}

	END_CRIT_SECTION();

	UnlockReleaseBuffer(fc_buf);

	relation_close(fc_seqrel, NoLock);
}

/*
 * 实现2参数setval过程。
 * 请参见do_setval进行讨论。
 */
Datum setval_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);
	int64		fc_next = PG_GETARG_INT64(1);

	fc_do_setval(fc_relid, fc_next, true);

	PG_RETURN_INT64(fc_next);
}

/*
 * 实现3参数setval过程。
 * 请参见do_setval进行讨论。
 */
Datum setval3_oid(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);
	int64		fc_next = PG_GETARG_INT64(1);
	bool		fc_iscalled = PG_GETARG_BOOL(2);

	fc_do_setval(fc_relid, fc_next, fc_iscalled);

	PG_RETURN_INT64(fc_next);
}


/*
 * 打开序列并在需要时获取锁
 *
 * 如果我们在此事务中尚未接触该序列，
 * 我们需要获取锁。我们安排让锁
 * 由顶级事务拥有，以便我们在每次xact中
 * 不需要多做一次。
 */
static Relation fc_lock_and_open_sequence(SeqTable fc_seq)
{
	LocalTransactionId fc_thislxid = MyProc->lxid;

	/* 如果在此xact中尚未拥有锁，则获取锁 */
	if (fc_seq->lxid != fc_thislxid)
	{
		ResourceOwner fc_currentOwner;

		fc_currentOwner = CurrentResourceOwner;
		CurrentResourceOwner = TopTransactionResourceOwner;

		LockRelationOid(fc_seq->relid, RowExclusiveLock);

		CurrentResourceOwner = fc_currentOwner;

		/* 标记我们在当前xact中拥有锁 */
		fc_seq->lxid = fc_thislxid;
	}

	/* 我们现在知道我们拥有锁，可以安全地打开关系 */
	return relation_open(fc_seq->relid, NoLock);
}

/*
 * 创建用于存储序列数据的哈希表
 */
static void fc_create_seq_hashtable(void)
{
	HASHCTL		fc_ctl;

	fc_ctl.keysize = sizeof(Oid);
	fc_ctl.entrysize = sizeof(SeqTableData);

	seqhashtab = hash_create("Sequence values", 16, &fc_ctl,
							 HASH_ELEM | HASH_BLOBS);
}

/*
 * 给定关系OID，打开并锁定序列。p_elm和p_rel是
 * 输出参数。
 */
static void fc_init_sequence(Oid fc_relid, SeqTable *fc_p_elm, Relation *fc_p_rel)
{
	SeqTable	fc_elm;
	Relation	fc_seqrel;
	bool		fc_found;

	/* 寻找或创建此序列的哈希表条目 */
	if (seqhashtab == NULL)
		fc_create_seq_hashtable();

	fc_elm = (SeqTable) hash_search(seqhashtab, &fc_relid, HASH_ENTER, &fc_found);

	/*
	 * 如果新哈希表条目之前不存在，则初始化它。
	 *
	 * 注意：seqhashtab 条目在后端生命周期内存储（除非
	 * 明确地通过 DISCARD 丢弃）。如果序列本身被删除，
	 * 那么条目将变成浪费的内存，但它足够小，以至于这
	 * 不会造成太大影响。
	 */
	if (!fc_found)
	{
		/* relid 已经填写 */
		fc_elm->filenode = InvalidOid;
		fc_elm->lxid = InvalidLocalTransactionId;
		fc_elm->last_valid = false;
		fc_elm->last = fc_elm->cached = 0;
	}

	/*
	 * 打开序列关系。
	 */
	fc_seqrel = fc_lock_and_open_sequence(fc_elm);

	if (fc_seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a sequence",
						RelationGetRelationName(fc_seqrel))));

	/*
	 * 如果序列自上次看到以来已被事务性替换，
	 * 则丢弃任何缓存但未发出的值。但是我们不接触 currval()
	 * 状态。
	 */
	if (fc_seqrel->rd_rel->relfilenode != fc_elm->filenode)
	{
		fc_elm->filenode = fc_seqrel->rd_rel->relfilenode;
		fc_elm->cached = fc_elm->last;
	}

	/* 返回结果 */
	*fc_p_elm = fc_elm;
	*fc_p_rel = fc_seqrel;
}


/*
 * 在打开的序列关系下，锁定页面缓冲区并找到元组
 *
 * *buf 接收固定并已锁定缓冲区的引用
 * *seqdatatuple 接收序列元组本身的引用
 *		（此参数应指向类型为 HeapTupleData 的局部变量）
 *
 * 函数的返回值指向元组的数据负载
 */
static Form_pg_sequence_data
fc_read_seq_tuple(Relation fc_rel, Buffer *fc_buf, HeapTuple fc_seqdatatuple)
{
	Page		fc_page;
	ItemId		fc_lp;
	sequence_magic *fc_sm;
	Form_pg_sequence_data fc_seq;

	*fc_buf = ReadBuffer(fc_rel, 0);
	LockBuffer(*fc_buf, BUFFER_LOCK_EXCLUSIVE);

	fc_page = BufferGetPage(*fc_buf);
	fc_sm = (sequence_magic *) PageGetSpecialPointer(fc_page);

	if (fc_sm->magic != SEQ_MAGIC)
		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
			 RelationGetRelationName(fc_rel), fc_sm->magic);

	fc_lp = PageGetItemId(fc_page, FirstOffsetNumber);
	Assert(ItemIdIsNormal(fc_lp));

	/* 注意我们目前只设置 *seqdatatuple 的这两个字段 */
	fc_seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(fc_page, fc_lp);
	fc_seqdatatuple->t_len = ItemIdGetLength(fc_lp);

	/*
	 * 以前的 Postgres 版本未能防止对序列进行 SELECT FOR UPDATE，
	 * 这会在序列元组的 xmax 中留下一个未冻结的 XID，
	 * 这最终会导致 clog 访问失败或更糟的情况。如果我们
	 * 看到这种情况发生，请进行清理。我们将其视为提示
	 * 位更新，即，不用麻烦 WAL 日志化，因为如果更新丢失，
	 * 我们当然可以再次做到这一点。
	 */
	Assert(!(fc_seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
	if (HeapTupleHeaderGetRawXmax(fc_seqdatatuple->t_data) != InvalidTransactionId)
	{
		HeapTupleHeaderSetXmax(fc_seqdatatuple->t_data, InvalidTransactionId);
		fc_seqdatatuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
		fc_seqdatatuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
		MarkBufferDirtyHint(*fc_buf, true);
	}

	fc_seq = (Form_pg_sequence_data) GETSTRUCT(fc_seqdatatuple);

	return fc_seq;
}

/*
 * init_params：处理 CREATE 或 ALTER SEQUENCE 的选项列表，并
 * 将值存储到 seqform 的适当字段中，供进入 pg_sequence 目录的更改，
 * 以及 seqdataform 的字段以进行对序列关系本身的更改。如果我们更改了
 * 任何需要重写序列关系的参数（对 ALTER SEQUENCE 有趣），
 * 则将 *need_seq_rewrite 设置为 true。还将 *owned_by 设置为
 * 任何 OWNED BY 选项，或者如果没有则设置为 NIL。
 *
 * 如果 isInit 为 true，则用默认值填充任何未指定的选项；
 * 否则，不更改未明确覆盖的现有选项。
 *
 * 注意：每当我们更改影响未来序列值生成的参数时，
 * 即使 seqdataform 本身未更改，我们也强制进行序列重写。
 * 这使得 ALTER SEQUENCE 可以进行事务处理。目前，
 * 唯一不会导致这种情况的选项是 OWNED BY。对于 ALTER SEQUENCE
 * OWNED BY 不重写序列是 *必要的*，因为这会通过导致
 * 序列的 relfilenode 发生不必要的变化来破坏 pg_upgrade。
 */
static void fc_init_params(ParseState *fc_pstate, List *fc_options, bool fc_for_identity,
			bool fc_isInit,
			Form_pg_sequence fc_seqform,
			Form_pg_sequence_data fc_seqdataform,
			bool *fc_need_seq_rewrite,
			List **fc_owned_by)
{
	DefElem    *fc_as_type = NULL;
	DefElem    *fc_start_value = NULL;
	DefElem    *fc_restart_value = NULL;
	DefElem    *fc_increment_by = NULL;
	DefElem    *fc_max_value = NULL;
	DefElem    *fc_min_value = NULL;
	DefElem    *fc_cache_value = NULL;
	DefElem    *fc_is_cycled = NULL;
	ListCell   *fc_option;
	bool		fc_reset_max_value = false;
	bool		fc_reset_min_value = false;

	*fc_need_seq_rewrite = false;
	*fc_owned_by = NIL;

	foreach(fc_option, fc_options)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_option);

		if (strcmp(fc_defel->defname, "as") == 0)
		{
			if (fc_as_type)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_as_type = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "increment") == 0)
		{
			if (fc_increment_by)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_increment_by = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "start") == 0)
		{
			if (fc_start_value)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_start_value = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "restart") == 0)
		{
			if (fc_restart_value)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_restart_value = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "maxvalue") == 0)
		{
			if (fc_max_value)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_max_value = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "minvalue") == 0)
		{
			if (fc_min_value)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_min_value = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "cache") == 0)
		{
			if (fc_cache_value)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_cache_value = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "cycle") == 0)
		{
			if (fc_is_cycled)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_is_cycled = fc_defel;
			*fc_need_seq_rewrite = true;
		}
		else if (strcmp(fc_defel->defname, "owned_by") == 0)
		{
			if (*fc_owned_by)
				errorConflictingDefElem(fc_defel, fc_pstate);
			*fc_owned_by = defGetQualifiedName(fc_defel);
		}
		else if (strcmp(fc_defel->defname, "sequence_name") == 0)
		{
			/*
			 * 解析器允许这样做，但仅适用于身份列，
			 * 在这种情况下它在 parse_utilcmd.c 中被过滤掉。我们只会
			 * 在有人将其放入 CREATE SEQUENCE 时到此，
			 * 在这种情况下它是多余的。（对于同样不标准的
			 * LOGGED 和 UNLOGGED 选项也是如此，但对于这些，下面的默认错误
			 * 似乎足够了。）
			 */
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("invalid sequence option SEQUENCE NAME"),
					 parser_errposition(fc_pstate, fc_defel->location)));
		}
		else
			elog(ERROR, "option \"%s\" not recognized",
				 fc_defel->defname);
	}

	/*
	 * 当isInit或改变任何会影响未来nextval分配的参数时，必须重置log_cnt。
	 */
	if (fc_isInit)
		fc_seqdataform->log_cnt = 0;

	/* AS 类型 */
	if (fc_as_type != NULL)
	{
		Oid			fc_newtypid = typenameTypeId(fc_pstate, defGetTypeName(fc_as_type));

		if (fc_newtypid != INT2OID &&
			fc_newtypid != INT4OID &&
			fc_newtypid != INT8OID)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 fc_for_identity
					 ? errmsg("identity column type must be smallint, integer, or bigint")
					 : errmsg("sequence type must be smallint, integer, or bigint")));

		if (!fc_isInit)
		{
			/*
			 * 更改类型时，如果旧序列的最小/最大值是旧类型的最小/最大值，
			 * 则将序列的最小/最大值调整为新类型的最小/最大值。
			 * （否则，用户选择了显式的最小/最大值，我们将保持不变。）
			 */
			if ((fc_seqform->seqtypid == INT2OID && fc_seqform->seqmax == PG_INT16_MAX) ||
				(fc_seqform->seqtypid == INT4OID && fc_seqform->seqmax == PG_INT32_MAX) ||
				(fc_seqform->seqtypid == INT8OID && fc_seqform->seqmax == PG_INT64_MAX))
				fc_reset_max_value = true;
			if ((fc_seqform->seqtypid == INT2OID && fc_seqform->seqmin == PG_INT16_MIN) ||
				(fc_seqform->seqtypid == INT4OID && fc_seqform->seqmin == PG_INT32_MIN) ||
				(fc_seqform->seqtypid == INT8OID && fc_seqform->seqmin == PG_INT64_MIN))
				fc_reset_min_value = true;
		}

		fc_seqform->seqtypid = fc_newtypid;
	}
	else if (fc_isInit)
	{
		fc_seqform->seqtypid = INT8OID;
	}

	/* 增量 */
	if (fc_increment_by != NULL)
	{
		fc_seqform->seqincrement = defGetInt64(fc_increment_by);
		if (fc_seqform->seqincrement == 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("INCREMENT must not be zero")));
		fc_seqdataform->log_cnt = 0;
	}
	else if (fc_isInit)
	{
		fc_seqform->seqincrement = 1;
	}

	/* 循环 */
	if (fc_is_cycled != NULL)
	{
		fc_seqform->seqcycle = boolVal(fc_is_cycled->arg);
		Assert(BoolIsValid(fc_seqform->seqcycle));
		fc_seqdataform->log_cnt = 0;
	}
	else if (fc_isInit)
	{
		fc_seqform->seqcycle = false;
	}

	/* 最大值（null参数表示无最大值） */
	if (fc_max_value != NULL && fc_max_value->arg)
	{
		fc_seqform->seqmax = defGetInt64(fc_max_value);
		fc_seqdataform->log_cnt = 0;
	}
	else if (fc_isInit || fc_max_value != NULL || fc_reset_max_value)
	{
		if (fc_seqform->seqincrement > 0 || fc_reset_max_value)
		{
			/* 升序序列 */
			if (fc_seqform->seqtypid == INT2OID)
				fc_seqform->seqmax = PG_INT16_MAX;
			else if (fc_seqform->seqtypid == INT4OID)
				fc_seqform->seqmax = PG_INT32_MAX;
			else
				fc_seqform->seqmax = PG_INT64_MAX;
		}
		else
			fc_seqform->seqmax = -1;	/* 降序序列 */
		fc_seqdataform->log_cnt = 0;
	}

	/* 验证最大值。无需检查INT8，因为seqmax是int64 */
	if ((fc_seqform->seqtypid == INT2OID && (fc_seqform->seqmax < PG_INT16_MIN || fc_seqform->seqmax > PG_INT16_MAX))
		|| (fc_seqform->seqtypid == INT4OID && (fc_seqform->seqmax < PG_INT32_MIN || fc_seqform->seqmax > PG_INT32_MAX)))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("MAXVALUE (%lld) is out of range for sequence data type %s",
						(long long) fc_seqform->seqmax,
						format_type_be(fc_seqform->seqtypid))));

	/* 最小值（null参数表示无最小值） */
	if (fc_min_value != NULL && fc_min_value->arg)
	{
		fc_seqform->seqmin = defGetInt64(fc_min_value);
		fc_seqdataform->log_cnt = 0;
	}
	else if (fc_isInit || fc_min_value != NULL || fc_reset_min_value)
	{
		if (fc_seqform->seqincrement < 0 || fc_reset_min_value)
		{
			/* 降序序列 */
			if (fc_seqform->seqtypid == INT2OID)
				fc_seqform->seqmin = PG_INT16_MIN;
			else if (fc_seqform->seqtypid == INT4OID)
				fc_seqform->seqmin = PG_INT32_MIN;
			else
				fc_seqform->seqmin = PG_INT64_MIN;
		}
		else
			fc_seqform->seqmin = 1;	/* 升序序列 */
		fc_seqdataform->log_cnt = 0;
	}

	/* 验证最小值。无需检查INT8，因为seqmin是int64 */
	if ((fc_seqform->seqtypid == INT2OID && (fc_seqform->seqmin < PG_INT16_MIN || fc_seqform->seqmin > PG_INT16_MAX))
		|| (fc_seqform->seqtypid == INT4OID && (fc_seqform->seqmin < PG_INT32_MIN || fc_seqform->seqmin > PG_INT32_MAX)))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("MINVALUE (%lld) is out of range for sequence data type %s",
						(long long) fc_seqform->seqmin,
						format_type_be(fc_seqform->seqtypid))));

	/* 交叉检查最小/最大 */
	if (fc_seqform->seqmin >= fc_seqform->seqmax)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("MINVALUE (%lld) must be less than MAXVALUE (%lld)",
						(long long) fc_seqform->seqmin,
						(long long) fc_seqform->seqmax)));

	/* 从 */
	if (fc_start_value != NULL)
	{
		fc_seqform->seqstart = defGetInt64(fc_start_value);
	}
	else if (fc_isInit)
	{
		if (fc_seqform->seqincrement > 0)
			fc_seqform->seqstart = fc_seqform->seqmin;	/* 升序序列 */
		else
			fc_seqform->seqstart = fc_seqform->seqmax;	/* 降序序列 */
	}

	/* 交叉检查起始值 */
	if (fc_seqform->seqstart < fc_seqform->seqmin)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("START value (%lld) cannot be less than MINVALUE (%lld)",
						(long long) fc_seqform->seqstart,
						(long long) fc_seqform->seqmin)));
	if (fc_seqform->seqstart > fc_seqform->seqmax)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("START value (%lld) cannot be greater than MAXVALUE (%lld)",
						(long long) fc_seqform->seqstart,
						(long long) fc_seqform->seqmax)));

	/* 重新开始 [WITH] */
	if (fc_restart_value != NULL)
	{
		if (fc_restart_value->arg != NULL)
			fc_seqdataform->last_value = defGetInt64(fc_restart_value);
		else
			fc_seqdataform->last_value = fc_seqform->seqstart;
		fc_seqdataform->is_called = false;
		fc_seqdataform->log_cnt = 0;
	}
	else if (fc_isInit)
	{
		fc_seqdataform->last_value = fc_seqform->seqstart;
		fc_seqdataform->is_called = false;
	}

	/* 交叉检查重新开始（或当前值，如果更改最小/最大） */
	if (fc_seqdataform->last_value < fc_seqform->seqmin)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("RESTART value (%lld) cannot be less than MINVALUE (%lld)",
						(long long) fc_seqdataform->last_value,
						(long long) fc_seqform->seqmin)));
	if (fc_seqdataform->last_value > fc_seqform->seqmax)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("RESTART value (%lld) cannot be greater than MAXVALUE (%lld)",
						(long long) fc_seqdataform->last_value,
						(long long) fc_seqform->seqmax)));

	/* 缓存 */
	if (fc_cache_value != NULL)
	{
		fc_seqform->seqcache = defGetInt64(fc_cache_value);
		if (fc_seqform->seqcache <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("CACHE (%lld) must be greater than zero",
							(long long) fc_seqform->seqcache)));
		fc_seqdataform->log_cnt = 0;
	}
	else if (fc_isInit)
	{
		fc_seqform->seqcache = 1;
	}
}

/*
 * 处理CREATE/ALTER SEQUENCE的OWNED BY选项
 *
 * 序列的所有权权限已被检查，
 * 但如果我们建立新的owned-by依赖关系，
 * 必须强制引用的表与序列具有相同的所有者和命名空间。
 */
static void fc_process_owned_by(Relation fc_seqrel, List *fc_owned_by, bool fc_for_identity)
{
	DependencyType fc_deptype;
	int			fc_nnames;
	Relation	fc_tablerel;
	AttrNumber	fc_attnum;

	fc_deptype = fc_for_identity ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO;

	fc_nnames = list_length(fc_owned_by);
	Assert(fc_nnames > 0);
	if (fc_nnames == 1)
	{
		/* 必须是OWNED BY NONE */
		if (strcmp(strVal(linitial(fc_owned_by)), "none") != 0)
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("invalid OWNED BY option"),
					 errhint("Specify OWNED BY table.column or OWNED BY NONE.")));
		fc_tablerel = NULL;
		fc_attnum = 0;
	}
	else
	{
		List	   *fc_relname;
		char	   *fc_attrname;
		RangeVar   *fc_rel;

		/* 分开表名和属性名 */
		fc_relname = list_truncate(list_copy(fc_owned_by), fc_nnames - 1);
		fc_attrname = strVal(llast(fc_owned_by));

		/* 打开并锁定关系以确保其在此期间不会消失 */
		fc_rel = makeRangeVarFromNameList(fc_relname);
		fc_tablerel = relation_openrv(fc_rel, AccessShareLock);

		/* 必须是常规或外部表 */
		if (!(fc_tablerel->rd_rel->relkind == RELKIND_RELATION ||
			  fc_tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
			  fc_tablerel->rd_rel->relkind == RELKIND_VIEW ||
			  fc_tablerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("sequence cannot be owned by relation \"%s\"",
							RelationGetRelationName(fc_tablerel)),
					 errdetail_relkind_not_supported(fc_tablerel->rd_rel->relkind)));

		/* 我们坚持相同的所有者和模式 */
		if (fc_seqrel->rd_rel->relowner != fc_tablerel->rd_rel->relowner)
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("sequence must have same owner as table it is linked to")));
		if (RelationGetNamespace(fc_seqrel) != RelationGetNamespace(fc_tablerel))
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("sequence must be in same schema as table it is linked to")));

		/* 现在，从系统缓存获取属性编号 */
		fc_attnum = get_attnum(RelationGetRelid(fc_tablerel), fc_attrname);
		if (fc_attnum == InvalidAttrNumber)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("column \"%s\" of relation \"%s\" does not exist",
							fc_attrname, RelationGetRelationName(fc_tablerel))));
	}

	/*
	 * 捕获用户明确在标识序列上运行OWNED BY的情况。
	 */
	if (fc_deptype == DEPENDENCY_AUTO)
	{
		Oid			fc_tableId;
		int32		fc_colId;

		if (sequenceIsOwned(RelationGetRelid(fc_seqrel), DEPENDENCY_INTERNAL, &fc_tableId, &fc_colId))
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot change ownership of identity sequence"),
					 errdetail("Sequence \"%s\" is linked to table \"%s\".",
							   RelationGetRelationName(fc_seqrel),
							   get_rel_name(fc_tableId))));
	}

	/*
	 * 好的，我们准备好更新pg_depend了。
	 * 首先删除序列的任何现有依赖项，然后可以选择添加一个新的依赖项。
	 */
	deleteDependencyRecordsForClass(RelationRelationId, RelationGetRelid(fc_seqrel),
									RelationRelationId, fc_deptype);

	if (fc_tablerel)
	{
		ObjectAddress fc_refobject,
					fc_depobject;

		fc_refobject.classId = RelationRelationId;
		fc_refobject.objectId = RelationGetRelid(fc_tablerel);
		fc_refobject.objectSubId = fc_attnum;
		fc_depobject.classId = RelationRelationId;
		fc_depobject.objectId = RelationGetRelid(fc_seqrel);
		fc_depobject.objectSubId = 0;
		recordDependencyOn(&fc_depobject, &fc_refobject, fc_deptype);
	}

	/* 完成，但在提交之前保持锁定 */
	if (fc_tablerel)
		relation_close(fc_tablerel, NoLock);
}


/*
 * 以解析器创建的列表形式返回序列参数。
 */
List * sequence_options(Oid fc_relid)
{
	HeapTuple	fc_pgstuple;
	Form_pg_sequence fc_pgsform;
	List	   *fc_options = NIL;

	fc_pgstuple = SearchSysCache1(SEQRELID, fc_relid);
	if (!HeapTupleIsValid(fc_pgstuple))
		elog(ERROR, "cache lookup failed for sequence %u", fc_relid);
	fc_pgsform = (Form_pg_sequence) GETSTRUCT(fc_pgstuple);

	/* 对于64位整数，请使用makeFloat()，就像gram.y那样。 */
	fc_options = lappend(fc_options,
					  makeDefElem("cache", (Node *) makeFloat(psprintf(INT64_FORMAT, fc_pgsform->seqcache)), -1));
	fc_options = lappend(fc_options,
					  makeDefElem("cycle", (Node *) makeBoolean(fc_pgsform->seqcycle), -1));
	fc_options = lappend(fc_options,
					  makeDefElem("increment", (Node *) makeFloat(psprintf(INT64_FORMAT, fc_pgsform->seqincrement)), -1));
	fc_options = lappend(fc_options,
					  makeDefElem("maxvalue", (Node *) makeFloat(psprintf(INT64_FORMAT, fc_pgsform->seqmax)), -1));
	fc_options = lappend(fc_options,
					  makeDefElem("minvalue", (Node *) makeFloat(psprintf(INT64_FORMAT, fc_pgsform->seqmin)), -1));
	fc_options = lappend(fc_options,
					  makeDefElem("start", (Node *) makeFloat(psprintf(INT64_FORMAT, fc_pgsform->seqstart)), -1));

	ReleaseSysCache(fc_pgstuple);

	return fc_options;
}

/*
 * 返回序列参数（以前用于信息模式）
 */
Datum pg_sequence_parameters(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);
	TupleDesc	fc_tupdesc;
	Datum		fc_values[7];
	bool		fc_isnull[7];
	HeapTuple	fc_pgstuple;
	Form_pg_sequence fc_pgsform;

	if (pg_class_aclcheck(fc_relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied for sequence %s",
						get_rel_name(fc_relid))));

	fc_tupdesc = CreateTemplateTupleDesc(7);
	TupleDescInitEntry(fc_tupdesc, (AttrNumber) 1, "start_value",
					   INT8OID, -1, 0);
	TupleDescInitEntry(fc_tupdesc, (AttrNumber) 2, "minimum_value",
					   INT8OID, -1, 0);
	TupleDescInitEntry(fc_tupdesc, (AttrNumber) 3, "maximum_value",
					   INT8OID, -1, 0);
	TupleDescInitEntry(fc_tupdesc, (AttrNumber) 4, "increment",
					   INT8OID, -1, 0);
	TupleDescInitEntry(fc_tupdesc, (AttrNumber) 5, "cycle_option",
					   BOOLOID, -1, 0);
	TupleDescInitEntry(fc_tupdesc, (AttrNumber) 6, "cache_size",
					   INT8OID, -1, 0);
	TupleDescInitEntry(fc_tupdesc, (AttrNumber) 7, "data_type",
					   OIDOID, -1, 0);

	BlessTupleDesc(fc_tupdesc);

	memset(fc_isnull, 0, sizeof(fc_isnull));

	fc_pgstuple = SearchSysCache1(SEQRELID, fc_relid);
	if (!HeapTupleIsValid(fc_pgstuple))
		elog(ERROR, "cache lookup failed for sequence %u", fc_relid);
	fc_pgsform = (Form_pg_sequence) GETSTRUCT(fc_pgstuple);

	fc_values[0] = Int64GetDatum(fc_pgsform->seqstart);
	fc_values[1] = Int64GetDatum(fc_pgsform->seqmin);
	fc_values[2] = Int64GetDatum(fc_pgsform->seqmax);
	fc_values[3] = Int64GetDatum(fc_pgsform->seqincrement);
	fc_values[4] = BoolGetDatum(fc_pgsform->seqcycle);
	fc_values[5] = Int64GetDatum(fc_pgsform->seqcache);
	fc_values[6] = ObjectIdGetDatum(fc_pgsform->seqtypid);

	ReleaseSysCache(fc_pgstuple);

	return HeapTupleGetDatum(heap_form_tuple(fc_tupdesc, fc_values, fc_isnull));
}

/*
 * 返回序列的最后一个值
 *
 * 注意：这与lastval()的含义完全不同。
 */
Datum pg_sequence_last_value(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);
	SeqTable	fc_elm;
	Relation	fc_seqrel;
	bool		fc_is_called = false;
	int64		fc_result = 0;

	/* 打开并锁定序列 */
	fc_init_sequence(fc_relid, &fc_elm, &fc_seqrel);

	if (pg_class_aclcheck(fc_relid, GetUserId(), ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied for sequence %s",
						RelationGetRelationName(fc_seqrel))));

	/*
	 * 对于其他会话的临时序列，我们返回NULL。
	 * pg_sequences系统视图已经过滤掉这些序列，但这为
	 * 如果有人直接调用此函数提供了防御，以避免ERROR。
	 *
	 * 此外，为了pg_sequences视图的利益，我们对备用中的
	 * 无日志序列返回NULL，而不是抛出错误。
	 */
	if (!RELATION_IS_OTHER_TEMP(fc_seqrel) &&
		(RelationIsPermanent(fc_seqrel) || !RecoveryInProgress()))
	{
		Buffer		fc_buf;
		HeapTupleData fc_seqtuple;
		Form_pg_sequence_data fc_seq;

		fc_seq = fc_read_seq_tuple(fc_seqrel, &fc_buf, &fc_seqtuple);

		fc_is_called = fc_seq->is_called;
		fc_result = fc_seq->last_value;

		UnlockReleaseBuffer(fc_buf);
	}
	relation_close(fc_seqrel, NoLock);

	if (fc_is_called)
		PG_RETURN_INT64(fc_result);
	else
		PG_RETURN_NULL();
}


void seq_redo(XLogReaderState *fc_record)
{
	XLogRecPtr	fc_lsn = fc_record->EndRecPtr;
	uint8		fc_info = XLogRecGetInfo(fc_record) & ~XLR_INFO_MASK;
	Buffer		fc_buffer;
	Page		fc_page;
	Page		fc_localpage;
	char	   *fc_item;
	Size		fc_itemsz;
	xl_seq_rec *fc_xlrec = (xl_seq_rec *) XLogRecGetData(fc_record);
	sequence_magic *fc_sm;

	if (fc_info != XLOG_SEQ_LOG)
		elog(PANIC, "seq_redo: unknown op code %u", fc_info);

	fc_buffer = XLogInitBufferForRedo(fc_record, 0);
	fc_page = (Page) BufferGetPage(fc_buffer);

	/*
	 * 我们总是重新初始化页面。然而，由于这种WAL记录类型也
	 * 用于更新序列，因此可能有热备份后端正在并发查看该页面；
	 * 所以我们不能暂时破坏缓冲区。解决方案是在本地工作区内
	 * 构建正确的新页面内容，然后 memcpy 到缓冲区中。这样仅
	 * 有应该更改的字节会更改，即使是暂时的。出于对齐的原因，
	 * 我们必须为本地页面分配内存。
	 */
	fc_localpage = (Page) palloc(BufferGetPageSize(fc_buffer));

	PageInit(fc_localpage, BufferGetPageSize(fc_buffer), sizeof(sequence_magic));
	fc_sm = (sequence_magic *) PageGetSpecialPointer(fc_localpage);
	fc_sm->magic = SEQ_MAGIC;

	fc_item = (char *) fc_xlrec + sizeof(xl_seq_rec);
	fc_itemsz = XLogRecGetDataLen(fc_record) - sizeof(xl_seq_rec);

	if (PageAddItem(fc_localpage, (Item) fc_item, fc_itemsz,
					FirstOffsetNumber, false, false) == InvalidOffsetNumber)
		elog(PANIC, "seq_redo: failed to add item to page");

	PageSetLSN(fc_localpage, fc_lsn);

	memcpy(fc_page, fc_localpage, BufferGetPageSize(fc_buffer));
	MarkBufferDirty(fc_buffer);
	UnlockReleaseBuffer(fc_buffer);

	pfree(fc_localpage);
}

/*
 * 刷新缓存的序列信息。
 */
void ResetSequenceCaches(void)
{
	if (seqhashtab)
	{
		hash_destroy(seqhashtab);
		seqhashtab = NULL;
	}

	last_used_seq = NULL;
}

/*
 * 在对序列页面执行一致性检查之前对其进行掩码处理。
 */
void seq_mask(char *fc_page, BlockNumber fc_blkno)
{
	mask_page_lsn_and_checksum(fc_page);

	mask_unused_space(fc_page);
}
